package cn.iocoder.yudao.module.vlog.info.service;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreLambdaQueryWrapper;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.region.RegionUtils;
import cn.iocoder.yudao.framework.region.enums.RegionType;
import cn.iocoder.yudao.module.api.vlog.info.enums.VLogStatusEnum;
import cn.iocoder.yudao.module.vlog.info.bo.VLogMyActionBO;
import cn.iocoder.yudao.module.vlog.info.convert.VLogInfoConvert;
import cn.iocoder.yudao.module.vlog.info.dal.dataobject.VLogFavoriteDO;
import cn.iocoder.yudao.module.vlog.info.dal.dataobject.VLogInfoDO;
import cn.iocoder.yudao.module.vlog.info.dal.dataobject.VLogLikeDO;
import cn.iocoder.yudao.module.vlog.info.dal.mysql.VLogFavoriteMapper;
import cn.iocoder.yudao.module.vlog.info.dal.mysql.VLogInfoMapper;
import cn.iocoder.yudao.module.vlog.info.dal.mysql.VLogLikeMapper;
import cn.iocoder.yudao.module.vlog.info.vo.*;
import cn.iocoder.yudao.module.vlog.product.dal.dataobject.VLogProductDO;
import cn.iocoder.yudao.module.vlog.product.dal.mysql.VLogProductMapper;
import cn.iocoder.yudao.module.vlog.statistics.service.VLogStatisticsService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.api.vlog.ErrorCodeConstants.*;

/**
 * 视频信息 Service 实现类
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Service
@Validated
public class VLogInfoServiceImpl implements VLogInfoService {

    @Resource
    private VLogInfoMapper vLogInfoMapper;
    @Resource
    private VLogProductMapper vLogProductMapper;
    @Resource
    private VLogLikeMapper vLogLikeMapper;
    @Resource
    private VLogFavoriteMapper vLogFavoriteMapper;
    @Resource
    private VLogStatisticsService vLogStatisticsService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createInfo(Long shopId, Long userId, AppVLogInfoCreateReqVO reqVO) {
        return saveVlogInfoAndProduct(shopId, userId, reqVO);
    }

    private Long saveVlogInfoAndProduct(Long shopId, Long userId, AppVLogInfoCreateReqVO reqVO) {
        VLogInfoDO infoDO = VLogInfoConvert.INSTANCE.convert(reqVO);
        infoDO.setShopId(shopId);
        infoDO.setUserId(userId);
        infoDO.setStatus(VLogStatusEnum.PENDING.getValue());
        infoDO.setCityId(RegionUtils.trimRegionId(reqVO.getCityId(), RegionType.CITY));
        vLogInfoMapper.insert(infoDO);
        VLogProductDO productDO = new VLogProductDO();
        productDO.setUserId(userId);
        productDO.setShopId(shopId);
        productDO.setVlogId(infoDO.getId());
        productDO.setSpuId(0L);
        vLogProductMapper.insert(productDO);
        vLogStatisticsService.increaseVLogCount(userId);
        return infoDO.getId();
    }

    @Override
    public void updateInfo(VLogInfoUpdateReqVO updateReqVO) {
        ensureInfoExists(updateReqVO.getId());
        VLogInfoDO infoDO = BeanUtils.toBean(updateReqVO, VLogInfoDO.class);
        vLogInfoMapper.updateById(infoDO);
    }

    @Override
    public void updateStatus(VLogInfoStatusUpdateReqVO updateReqVO) {
        VLogInfoDO infoDO = ensureInfoExists(updateReqVO.getId());
        infoDO.setStatus(updateReqVO.getStatus());
        vLogInfoMapper.updateById(infoDO);
    }

    @Override
    public void updatePrivacy(Long userId, AppVLogInfoPrivacyUpdateReqVO updateReqVO) {
        VLogInfoDO infoDO = ensureInfoExists(updateReqVO.getId());
        //permissionApi.checkWritePermission(userId, infoDO.getUserId());
        infoDO.setPrivacy(updateReqVO.getPrivacy());
        vLogInfoMapper.updateById(infoDO);
    }

    @Override
    public void deleteInfo(Long id) {
        VLogInfoDO infoDO = ensureInfoExists(id);
        doDelete(null, infoDO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteInfo(Long userId, Long id) {
        VLogInfoDO infoDO = ensureInfoExists(id);
        //permissionApi.checkWritePermission(userId, infoDO.getUserId());
        doDelete(userId, infoDO);
    }

    private void doDelete(Long userId, VLogInfoDO infoDO) {
        vLogInfoMapper.deleteById(infoDO.getId());
        // 删除短视频时解除产品关联
        vLogProductMapper.delete(new CoreLambdaQueryWrapper<VLogProductDO>()
                .eqIfPresent(VLogProductDO::getUserId, userId)
                .eq(VLogProductDO::getShopId, infoDO.getShopId())
                .eq(VLogProductDO::getVlogId, infoDO.getId()));
        vLogStatisticsService.decreaseVLogCount(userId);
    }

    @Override
    public VLogInfoDO ensureInfoExists(Long id) {
        if (id == null || id <= 0) {
            throw exception(BAD_REQUEST);
        }
        VLogInfoDO infoDO = vLogInfoMapper.selectById(id);
        if (infoDO == null) {
            throw exception(INFO_NOT_EXISTS);
        }
        return infoDO;
    }

    @Override
    public VLogInfoDO getInfo(Long id) {
        VLogInfoDO infoDO = vLogInfoMapper.selectById(id);
        if (infoDO == null) {
            throw exception(INFO_NOT_EXISTS);
        }
        return infoDO;
    }

    @Override
    public PageResult<VLogInfoDO> getInfoPage(VLogInfoPageReqVO pageReqVO) {
        return vLogInfoMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<VLogInfoDO> getEnabledInfoPage(AppVLogInfoPageReqVO pageReqVO) {
        return vLogInfoMapper.selectEnabledPage(pageReqVO);
    }

    @Override
    public PageResult<VLogInfoDO> getRecommendInfoPage(AppLatLngPageReqVO pageReqVO) {
        // 短视频推荐算法：暂时用随机排序
        Page<VLogInfoDO> page = vLogInfoMapper.selectRandomVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO.getCityId());
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getMyInfoPage(Long userId, AppVLogInfoPageReqVO pageReqVO) {
        return vLogInfoMapper.selectPage(userId, pageReqVO);
    }

    @Override
    public PageResult<VLogInfoDO> getLikedInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyLikedVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getFavoriteInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyFavoriteVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getFollowInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyFollowVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getFriendInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyFriendVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getCommentInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyCommentVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public PageResult<VLogInfoDO> getBrowseHistoryInfoPage(Long userId, PageParam pageReqVO) {
        Page<VLogInfoDO> page = vLogInfoMapper.selectMyBrowseHistoryVLogPage(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), userId);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public List<VLogMyActionBO> checkMyActions(Long userId, List<Long> ids) {
        if (userId == null || userId <= 0L) {
            return CollUtil.newArrayList();
        }
        if (CollUtil.isEmpty(ids)) {
            return CollUtil.newArrayList();
        }
        return vLogInfoMapper.selectMyVLogActions(userId, ids);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void likeVLog(Long userId, Long vlogId) {
        //memberUserApi.ensureUserExists(userId);
        VLogInfoDO infoDO = ensureInfoExists(vlogId);
        VLogLikeDO likedDO = vLogLikeMapper.selectOne(VLogLikeDO::getUserId, userId,
                VLogLikeDO::getVlogId, vlogId);
        if (likedDO == null) {
            // 插入
            likedDO = new VLogLikeDO();
            likedDO.setUserId(userId);
            likedDO.setVlogId(vlogId);
            likedDO.setCanceled(false);
            vLogLikeMapper.insert(likedDO);
        } else {
            likedDO.setCanceled(false);
            vLogLikeMapper.updateById(likedDO);
        }
        infoDO.setLikedCount(infoDO.getLikedCount() + 1);
        vLogInfoMapper.updateById(infoDO);
        vLogStatisticsService.increaseLikeCount(userId, infoDO.getUserId());
        // TODO 发送系统消息：某某用户点赞了短视频
        // TODO 记录用户的点赞行为
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlikeVLog(Long userId, Long vlogId) {
        //memberUserApi.ensureUserExists(userId);
        VLogInfoDO infoDO = ensureInfoExists(vlogId);
        VLogLikeDO likedDO = vLogLikeMapper.selectOne(VLogLikeDO::getUserId, userId,
                VLogLikeDO::getVlogId, vlogId);
        if (likedDO == null) {
            throw exception(LIKED_NOT_EXISTS);
        }
        likedDO.setCanceled(true);
        vLogLikeMapper.updateById(likedDO);
        infoDO.setLikedCount(Math.max(0, infoDO.getLikedCount() - 1));
        vLogInfoMapper.updateById(infoDO);
        vLogStatisticsService.decreaseLikeCount(userId, infoDO.getUserId());
        // TODO 记录用户的消赞行为
    }

    @Override
    public List<Long> getVLogLikedUserIds(Long vlogId) {
        List<VLogLikeDO> list = vLogLikeMapper.selectList(VLogLikeDO::getVlogId, vlogId,
                VLogLikeDO::getCanceled, false);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return list.stream().map(VLogLikeDO::getUserId).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void collectVLog(Long userId, Long vlogId) {
        //memberUserApi.ensureUserExists(userId);
        VLogInfoDO infoDO = ensureInfoExists(vlogId);
        VLogFavoriteDO favoriteDO = vLogFavoriteMapper.selectOne(VLogFavoriteDO::getUserId, userId,
                VLogFavoriteDO::getVlogId, vlogId);
        if (favoriteDO == null) {
            // 插入
            favoriteDO = new VLogFavoriteDO();
            favoriteDO.setUserId(userId);
            favoriteDO.setVlogId(vlogId);
            favoriteDO.setCanceled(false);
            vLogFavoriteMapper.insert(favoriteDO);
        } else {
            favoriteDO.setCanceled(false);
            vLogFavoriteMapper.updateById(favoriteDO);
        }
        infoDO.setFavoriteCount(infoDO.getFavoriteCount() + 1);
        vLogInfoMapper.updateById(infoDO);
        vLogStatisticsService.increaseFavoriteCount(userId, infoDO.getUserId());
        // TODO 发送系统消息：某某用户收藏了短视频
        // TODO 记录用户的收藏行为
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void uncollectVLog(Long userId, Long vlogId) {
        //memberUserApi.ensureUserExists(userId);
        VLogInfoDO infoDO = ensureInfoExists(vlogId);
        VLogFavoriteDO favoriteDO = vLogFavoriteMapper.selectOne(VLogFavoriteDO::getUserId, userId,
                VLogFavoriteDO::getVlogId, vlogId);
        if (favoriteDO == null) {
            throw exception(FAVORITE_NOT_EXISTS);
        }
        favoriteDO.setCanceled(true);
        vLogFavoriteMapper.updateById(favoriteDO);
        infoDO.setFavoriteCount(Math.max(0, infoDO.getFavoriteCount() - 1));
        vLogInfoMapper.updateById(infoDO);
        vLogStatisticsService.decreaseFavoriteCount(userId, infoDO.getUserId());
        // TODO 记录用户的消藏行为
    }

    @Override
    public List<Long> getVLogCollectUserIds(Long vlogId) {
        List<VLogFavoriteDO> list = vLogFavoriteMapper.selectList(VLogFavoriteDO::getVlogId, vlogId,
                VLogFavoriteDO::getCanceled, false);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return list.stream().map(VLogFavoriteDO::getUserId).collect(Collectors.toList());
    }

    @Override
    public List<VLogInfoDO> getVLogInfoList(Set<Long> vlogIds) {
        List<VLogInfoDO> list = vLogInfoMapper.selectList(new CoreLambdaQueryWrapper<VLogInfoDO>()
                .in(VLogInfoDO::getId, vlogIds));
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return list;
    }

}
